#include "scanner.h"


typedef struct Scanner_s {
	char* start;
	char* cur;
	int line;
}Scanner_t;

Scanner_t scanner;

/*********************************************************************************
**********                 准备辅助函数                              *************
***********************************************************************************/
//查看当前的字符
char peek() {
	return *scanner.cur;
}
//判断字符是不是字母或者下滑杠
bool is_letter_or_() {
	char ch = peek();
	return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch == '_';
}
//判断字符是不是数字
bool is_number() {
	char ch = peek();
	return ch >= '0' && ch <= '9';
}
//判断字符是不是空字符
bool is_empty() {
	char ch = peek();
	return ch == '\0';
}
//判断字符是否满足预期
bool is_match(char* ch, char expectation) {
	return *ch == expectation;
}
//指针加加
void pointer_add() {
	scanner.cur++;
}
//行加加
void line_add() {
	scanner.line++;
}


/*********************************************************************************
**********                 无效字符处理                              *************
***********************************************************************************/
//跳过\r,\t,\n,' ',//还有预处理包含
static void skip_invalid_content() {
	while (1) {
		char ch = peek();//拿到当前字符
		switch (ch)
		{
		case '\r':
		case '\t':
		case ' ':
			scanner.cur++;
			break;
		case '\n':
			scanner.cur++;
			scanner.line++;
			break;
		case '/':
			if (*(scanner.cur + 1) == '/') {
				while (peek() != '\n') {
					if (peek() == '\0') {
						return;
					}
					pointer_add();
				}
				return;
			}
		default:
			return;
		}
	}
}


/*********************************************************************************
**********                     处理字符                              *************
***********************************************************************************/
//字符处理模式
static void is_character(Token* token) {
	while (peek() != '\'') {
		if (is_empty) {
			token->type = TOKEN_ERROR;
			return;
		}
		if (peek() == '\n') {
			token->type = TOKEN_ERROR;
			line_add();
			pointer_add();
			return;
		}
		token->length++;
		pointer_add();
	}
	pointer_add();
	token->length++;
	token->type = TOKEN_CHARACTER;
}


/*********************************************************************************
**********                处理字符串                                 *************
***********************************************************************************/
//字符串处理模式
static void is_string(Token* token) {
	while (peek() != '\"') {
		if (is_empty) {
			token->type = TOKEN_ERROR;
			return;
		}
		if (peek() == '\n') {
			token->type = TOKEN_ERROR;
			line_add();
			pointer_add();
			return;
		}
		token->length++;
		pointer_add();
	}
	pointer_add();
	token->length++;
	token->type = TOKEN_STRING;
}


/*********************************************************************************
**********                处理数字                                   *************
***********************************************************************************/
//数字模式处理
static void number_token(Token* token) {
	int decimal_point = 2;
	while (is_number()) {
		if (peek() == '.') {
			decimal_point--;
		}
		if (decimal_point == 0) {
			break;
		}
		pointer_add();
		token->length++;
	}
	if (peek() == '\n' || is_empty()) {
		token->type = TOKEN_ERROR;
		return;
	}
	token->type = TOKEN_NUMBER;
}


/*********************************************************************************
**********                 处理符号                                  *************
***********************************************************************************/
//匹配符号
static bool matching_symbolic(char* cur, char expectation_symbolic) {
	return *(cur + 1) == expectation_symbolic;
}
//处理双符号
static void double_symbolic(Token* token) {
	char* cur = token->start;
	switch (peek()) {
	case '+':
		if (matching_symbolic(cur, '+')) {
			token->length = 2;
			token->type = TOKEN_PLUS_PLUS;
			pointer_add();
			pointer_add();
		}
		else if (matching_symbolic(cur, '=')) {
			token->length = 2;
			token->type = TOKEN_PLUS_EQUAL;
			pointer_add();
			pointer_add();
		}
		else {
			token->length = 1;
			token->type = TOKEN_PLUS;
			pointer_add();
		}
		return;
	case '-':
		if (matching_symbolic(cur, '-')) {
			token->length = 2;
			token->type = TOKEN_MINUS_MINUS;
			pointer_add();
			pointer_add();
		}
		else if (matching_symbolic(cur, '=')) {
			token->length = 2;
			token->type = TOKEN_MINUS_EQUAL;
			pointer_add();
			pointer_add();
		}
		else if (matching_symbolic(cur, '>')) {
			token->length = 2;
			token->type = TOKEN_MINUS_GREATER;
			pointer_add();
			pointer_add();
		}
		else {
			token->length = 1;
			token->type = TOKEN_MINUS;
			pointer_add();
		}
		return;
	case '*':
		if (matching_symbolic(cur, '=')) {
			token->length = 2;
			token->type = TOKEN_STAR_EQUAL;
			pointer_add();
			pointer_add();
		}
		else {
			token->length = 1;
			token->type = TOKEN_STAR;
			pointer_add();
			}
			return;
	case '/':
		if (matching_symbolic(cur, '=')) {
			token->length = 2;
			token->type = TOKEN_SLASH_EQUAL;
			pointer_add();
			pointer_add();
		}
		else {
			token->length = 1;
			token->type = TOKEN_SLASH;
			pointer_add();
		}
		return;
	case '%':
		if (matching_symbolic(cur, '=')) {
			token->length = 2;
			token->type = TOKEN_PERCENT_EQUAL;
			pointer_add();
			pointer_add();
		}
		else {
			token->length = 1;
			token->type = TOKEN_PERCENT;
			pointer_add();
		}
		return;
	case '&':
		if (matching_symbolic(cur, '=')) {
			token->length = 2;
			token->type = TOKEN_AMPER_EQUAL;
			pointer_add();
			pointer_add();
		}
		 else if (matching_symbolic(cur, '&')) {
			token->length = 2;
			token->type = TOKEN_AMPER_AMPER;
			pointer_add();
			pointer_add();
		}
		else {
			token->length = 1;
			token->type = TOKEN_AMPER;
			pointer_add();
		}
		return;
	case '|':
		if (matching_symbolic(cur, '=')) {
			token->length = 2;
			token->type = TOKEN_PIPE_EQUAL;
			pointer_add();
			pointer_add();
		}
		else if (matching_symbolic(cur, '|')) {
			token->length = 2;
			token->type = TOKEN_PIPE_PIPE;
			pointer_add();
			pointer_add();
		}
		else {
			token->length = 1;
			token->type = TOKEN_PIPE;
			pointer_add();
		}
		return;
	case '^':
		if (matching_symbolic(cur, '=')) {
			token->length = 2;
			token->type = TOKEN_HAT_EQUAL;
			pointer_add();
			pointer_add();
		}
		else {
			token->length = 1;
			token->type = TOKEN_HAT;
			pointer_add();
		}
		return;
	case '=':
		if (matching_symbolic(cur, '=')) {
			token->length = 2;
			token->type = TOKEN_EQUAL_EQUAL;
			pointer_add();
			pointer_add();
		}
		else {
			token->length = 1;
			token->type = TOKEN_EQUAL;
			pointer_add();
		}
		return;
	case '!':
		if (matching_symbolic(cur, '=')) {
			token->length = 2;
			token->type = TOKEN_BANG_EQUAL;
			pointer_add();
			pointer_add();
		}
		else {
			token->length = 1;
			token->type = TOKEN_BANG;
			pointer_add();
		}
		return;
	case '<':
		if (matching_symbolic(cur, '=')) {
			token->length = 2;
			token->type = TOKEN_LESS_EQUAL;
			pointer_add();
			pointer_add();
		}
		else if (matching_symbolic(cur, '<')) {
			token->length = 2;
			token->type = TOKEN_LESS_LESS;
			pointer_add();
			pointer_add();
		}
		else {
			token->length = 1;
			token->type = TOKEN_LESS;
			pointer_add();
		}
		return;
	case '>':
		if (matching_symbolic(cur, '=')) {
			token->length = 2;
			token->type = TOKEN_GREATER_EQUAL;
			pointer_add();
			pointer_add();
		}
		else if (matching_symbolic(cur, '>')) {
			token->length = 2;
			token->type = TOKEN_GREATER_GREATER;
			pointer_add();
			pointer_add();
		}
		else {
			token->length = 1;
			token->type = TOKEN_GREATER;
			pointer_add();
		}
		return;
	default://剩下的为不可识别符号，为TOKEN_ERROR
		token->length = 1;
		pointer_add();
		return;
	}
}
//符号处理模式,先处理完单符号，再去处理双符号
static void symbolic_processing(Token* token) {
	char* cur = token->start;
	switch (*cur) {
	case '(':
		token->type = TOKEN_LEFT_PAREN;
		token->length = 1;
		pointer_add();
		return;
	case ')':
		token->type = TOKEN_RIGHT_PAREN;
		token->length = 1;
		pointer_add();
		return;
	case '[':
		token->type = TOKEN_LEFT_BRACKET;
		token->length = 1;
		pointer_add();
		return;
	case ']':
		token->type = TOKEN_RIGHT_BRACKET;
		token->length = 1;
		pointer_add();
		return;
	case '{':
		token->type = TOKEN_LEFT_BRACE;
		token->length = 1;
		pointer_add();
		return;
	case '}':
		token->type = TOKEN_RIGHT_BRACE;
		token->length = 1;
		pointer_add();
		return;
	case ',':
		token->type = TOKEN_COMMA;
		token->length = 1;
		pointer_add();
		return;
	case '.':
		token->type = TOKEN_DOT;
		token->length = 1;
		pointer_add();
		return;
	case ';':
		token->type = TOKEN_SEMICOLON;
		token->length = 1;
		pointer_add();
		return;
	case '~':
		token->type = TOKEN_TILDE;
		token->length = 1;
		pointer_add();
		return;
	default:
		double_symbolic(token);
		return;
	}
}


/*********************************************************************************
**********                 处理关键字和标识符                        *************
***********************************************************************************/
//匹配关键字
static TokenType match_keytword(char* start, char* keyword, int n,TokenType type) {
	return strncmp(start, keyword, n) == 0 ? type : TOKEN_IDENTIFIER;
}
//关键字或者标识符
static void identifiers_or_keywords(Token* token) {
	char* cur = token->start;//拿出字符串的首指针
	int len = token->length;
	switch (*cur) {
	case 'b':
		token->type = TOKEN_BREAK;
		token->type =  len == 5 ? match_keytword(cur + 1, "reak", 4, token->type) : TOKEN_IDENTIFIER;
		return;
	case 'c':
		switch (len) {
		case 4:
			if (*(cur + 1) == 'a') {
				token->type = TOKEN_CASE;
				token->type = match_keytword(cur + 2, "se", 2, token->type);
			}
			else {//不是case就是char或者标识符，此时第二个字符要匹配
				token->type = TOKEN_CHAR;
				token->type = match_keytword(cur + 1, "har", 3, token->type);
			}
			return;
		case 5:
			token->type = TOKEN_CONST;
			token->type = match_keytword(cur + 1, "onts", 4, token->type);
			return;
		case 8:
			token->type = TOKEN_CONTINUE;
			token->type = match_keytword(cur + 1, "ontinue", 7, token->type);
			return;
		default:
			token->type = TOKEN_IDENTIFIER;
			return;
		}
		
	case 'd':
		switch (len) {
		case 2:
			token->type = TOKEN_DO;
			token->type = match_keytword(cur + 1, "o", 1, token->type);
			return;
		case 6:
			token->type = TOKEN_DOUBLE;
			token->type = match_keytword(cur + 1, "ouble", 5, token->type);
			return;
		case 7:
			token->type = TOKEN_DEFAULT;
			token->type = match_keytword(cur + 1, "efault", 6, token->type);
			return;
		default:
			token->type = TOKEN_IDENTIFIER;
			return;
		}

	case 'e':
		if (len == 4) {
			if (*(cur + 1) == 'l') {//这是字母l
				token->type = TOKEN_ELSE;
				token->type = match_keytword(cur + 2, "se", 2, token->type);
			}
			else {//此时为enum或者标识符直接匹配第二个字符要匹配
				token->type = TOKEN_ENUM;
				token->type = match_keytword(cur + 1, "num", 3, token->type);
			}
		}
		else {
			token->type = TOKEN_IDENTIFIER;
		}
		return;
	case 'f':
		if (len == 3) {
			token->type = TOKEN_FOR;
			token->type = match_keytword(cur + 1, "or", 2, token->type);
		}
		else if (len == 5) {
			token->type = TOKEN_FLOAT;
			token->type = match_keytword(cur + 1, "loat", 4, token->type);
		}
		else {
			token->type = TOKEN_IDENTIFIER;
		}
		return;
	case 'g':
		token->type = TOKEN_GOTO;
		token->type = len == 4 ? match_keytword(cur + 1, "oto", 3, token->type) : TOKEN_IDENTIFIER;
		return;
	case 'i':
		if (len == 2) {
			token->type = TOKEN_IF;
			token->type = match_keytword(cur + 1, "f", 1, token->type);
		}
		else if (len == 3) {
			token->type = TOKEN_INT;
			token->type = match_keytword(cur + 1, "nt", 2, token->type);
		}
		else {
			token->type = TOKEN_IDENTIFIER;
		}
		return;
	case 'l'://字母l
		token->type = TOKEN_LONG;
		token->type = len == 4 ? match_keytword(cur + 1, "ong", 3, token->type) : TOKEN_IDENTIFIER;
		return;
	case 'r':
		token->type = TOKEN_RETURN;
		token->type = len == 6 ? match_keytword(cur + 1, "eturn", 5, token->type) : TOKEN_IDENTIFIER;
		return;
	case 's':
		if (len == 5) {
			token->type = TOKEN_SHORT;
			token->type = match_keytword(cur + 1, "hort", 4, token->type);
		}
		else if (len == 6) {
			switch (*(cur + 1)) {
			case 'i':
				if (*(cur + 2) == 'g') {
					token->type = TOKEN_SIGNED;
					token->type = match_keytword(cur + 3, "ned", 3, token->type);
				}
				else {//此时为sizeof或者标识符直接匹配注意第三个字符要匹配
					token->type = TOKEN_SIZEOF;
					token->type = match_keytword(cur + 2, "zeof", 4, token->type);
				}
				return;
			case 't':
				token->type = TOKEN_STRUCT;
				token->type = match_keytword(cur + 2, "ruct", 4, token->type);
				return;
			default://此时为switch或者标识符直接匹配
				token->type = TOKEN_SWITCH;
				token->type = match_keytword(cur + 1, "witch", 5, token->type);//注意要匹配第二个字符
				return;
			}
		}
		else {
			token->type = TOKEN_IDENTIFIER;
		}
		return;
	case 't':
		token->type = TOKEN_TYPEDEF;
		token->type = len == 7 ? match_keytword(cur + 1, "ypedef", 6, token->type) : TOKEN_IDENTIFIER;
		return;
	case 'u':
		if (len == 5) {
			token->type = TOKEN_UNION;
			token->type = match_keytword(cur + 1, "nion", 4, token->type);
		}
		else if (len == 8) {
			token->type = TOKEN_UNSIGNED;
			token->type = match_keytword(cur + 1, "nsigned", 7, token->type);
		}
		else {
			token->type = TOKEN_IDENTIFIER;
		}
		return;
	case 'v':
		token->type = TOKEN_VOID;
		token->type = len == 4 ? match_keytword(cur + 1, "oid", 3, token->type) : TOKEN_IDENTIFIER;
		return;
	case 'w':
		token->type = TOKEN_VOID;
		token->type = len == 5 ? match_keytword(cur + 1, "hile", 4, token->type) : TOKEN_IDENTIFIER;
		return;
	default:
		token->type = TOKEN_IDENTIFIER;
		return;
	}
}
//关键字和标识符处理模式
static void identifiers_and_keywords(Token* token) {
	while (is_letter_or_()) {
		if (peek() == '_') {
			token->type = TOKEN_IDENTIFIER;
		}
		pointer_add();
		token->length++;
	}
	if (token->type != TOKEN_IDENTIFIER) {
		identifiers_or_keywords(token);
	}
}


/*********************************************************************************
**********                 初始化全局变量scanner                     *************
***********************************************************************************/
// 对词法分析器Scanner 进行初始化 
//源代码字符串(这里涉及一个将源码转换成字符串的函数)
void initScanner(char* source) {
	scanner.cur = source;
	scanner.start = source;
	scanner.line = 1;
}


/*********************************************************************************
**********                 得到每个token                             *************
***********************************************************************************/
// 核心API, 调用scanToken(), 就生产一个Token, 也就是源代码中下一段字符数据的Token
// 当Token返回的是TOKEN_EOF时，源文件被消耗完毕，词法分析结束
Token scanToken() {
	skip_invalid_content();//跳过无效字符
	
	Token *token = calloc(1,sizeof(Token));
	if (token == NULL) {
		printf("calloc failed in Token scanToken()\n");
		exit(-1);
	}
	
	token->type = TOKEN_ERROR;
	token->length = 0;
	token->line = scanner.line;
	
	token->start = scanner.cur;
	scanner.start = scanner.cur;
	if (is_empty()) {//判断是不是空字符，是就说明已经读完了，结束了
		token->type = TOKEN_EOF;
		token->length++;
		return *token;
	}
	//判断是不是数字，是就进入数字模式
	else if (is_number()) {
		number_token(token);
	}
	//判断是不是字母，是就进入标识符和关键字模式
	else if (is_letter_or_()) {
		identifiers_and_keywords(token);
	}
	//判断是不是字符
	else if (peek() == '\'') {
		is_character(token);
	}
	//判断是不是字符串
	else if (peek() == '\"') {
		is_string(token);
	}
	//其余的就是符号或者不能识别的不能识别不修改初始化的错误类别即可
	else {
		symbolic_processing(token);
	}
	return *token;
}
